/************************************************************************
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Grid Technology Research Center,
National Institute of Advanced Industrial Science and Technology.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************/

package jp.aist.gtrc.plus.scheduler.specific.pbs.batch;

import java.io.*;
import java.util.*;

import jp.aist.gtrc.plus.scheduler.specific.pbs.batch.request.*;
import jp.aist.gtrc.plus.scheduler.specific.pbs.batch.reply.*;
import jp.aist.gtrc.plus.scheduler.specific.pbs.batch.request.BatchRequest;
import jp.aist.gtrc.plus.scheduler.specific.pbs.batch.request.BatchRequestAuthenticateUser;
import jp.aist.gtrc.plus.scheduler.specific.pbs.batch.request.BatchRequestHeader;
import jp.aist.gtrc.plus.scheduler.specific.pbs.util.PBSErrorCode;
import jp.aist.gtrc.plus.scheduler.specific.pbs.util.PBSException;

import org.apache.log4j.Logger;


public class DISDecoder{

	private String encodedData;
	private int index;

	protected static final  Logger logger = Logger.getLogger(DISDecoder.class);

	public DISDecoder() {

		reset();

	}

	private void reset(){

		encodedData = "";
		index = 0;

	}

	private void setEncodedData(String data){

		reset();
		if (data != null) {
			this.encodedData = data;
		}

	}

	private String getSub(int len) throws PBSException {

		assert(len > 0);
		if ((encodedData.length() - index) < len) {
			logger.debug("encoded data is too short or broken");
			throw new PBSException(PBSErrorCode.DISPROTO);
		}

		String sub = encodedData.substring(index, index + len);
		index += len;

		return sub;

	}

	private char getChar() throws PBSException {

		if (index >= encodedData.length()) {
			logger.debug("encoded data is too short or broken");
			throw new PBSException(PBSErrorCode.DISPROTO);
		}

		char c = encodedData.charAt(index);
		index++;

		return c;

	}

	public int getInt() throws PBSException {

		//	value	DIS			digit
		//	-14		"2-14"		2
		//	 -9		"-9"		1
		//	 -1		"-1"		1
		//	  0		"+0"		1
		//	  1		"+1"		1
		//	  9		"+9"		1
		//	 14		"2+14"		2
		//	123		"3+123"		3

		int digit;
		char c;
		boolean negative;

		c = getChar();
		if (Character.isDigit(c)) {
			digit = c - '0';
			assert(digit > 1);
			c = getChar();
		} else {
			digit = 1;
		}

		if (c == '+') {
			negative = false;
		} else if (c == '-') {
			negative = true;
		} else {
			throw new PBSException(PBSErrorCode.DISPROTO);
		}

		String value = getSub(digit);
		if (value == null) {
			throw new PBSException(PBSErrorCode.DISPROTO);
		}
		if (negative == false) {
			return Integer.parseInt(value);
		} else {
			return -Integer.parseInt(value);
		}

	}

	public String getString() throws PBSException{

		//	data			DIS format			length
		//	-------------------------------------------
		//	""				"+0"				0
		//	"Hello"			"+5Hello"			5
		//	"Hello, World"	"2+12Hello, World"	12

		int len = getInt();
		assert (len >= 0);

		if (len == 0) {
			return "";
		} else {
			return getSub(len);
		}

	}

	public BatchReply getDecodedReply(String data, BatchRequest request) throws PBSException {

		// First, decode header to know reply type
		setEncodedData(data);
		BatchReplyHeader header = new BatchReplyHeader(this);

		PBSErrorCode code = header.getErrorCode();
		if (code != PBSErrorCode.SUCCESS) {
			logger.debug(request.getRequestType().toString() + " failed: "
					+ code.toString());
			throw new PBSException(code);
		}

		// Make new reply with decoded header
		BatchReply reply = null;
		switch(header.getReplyType()) {
		case NULL:
			reply = new BatchReplyNull(header);
			break;
		case Status:
			switch(request.getRequestType()){
			case StatusServer:
				reply = new BatchReplyStatusServer(header);
				break;
			case StatusNode:
				reply = new BatchReplyStatusNode(header);
				break;
			case StatusQueue:
				reply = new BatchReplyStatusQueue(header);
				break;
			case SelectStatus:
				reply = new BatchReplyStatusSelect(header);
				break;
			default:
				assert(false);
				logger.info("not supported request type: " + request.getRequestType());
				break;
			}
			break;
		case RescQuery:
			reply = new BatchReplyResourceQuery(header);
			break;
		default:
			assert(false);
			logger.info("not supported reply type: " + header.getReplyType());
			throw new PBSException(PBSErrorCode.INTERNAL);
		}

		reply.decode(this);

		return reply;

	}

	public BatchRequest getDecodedRequest(String data) throws PBSException {

		// First, decode header to know request type
		setEncodedData(data);
		BatchRequestHeader header = new BatchRequestHeader(this);
		BatchRequest request;

		switch (header.getRequestType()) {
		case AuthenUser:
			request = new BatchRequestAuthenticateUser(header);
			break;
		default:
			request = null;
		}

		if (request != null) {
			request.decode(this);
		} else {
			logger.debug("Unsupported request received: " + header.getRequestType());
		}

		return request;

	}

        public static void main(String[] args) throws Exception {
           BatchRequest request = new BatchRequestSelectStatus("xxx");
           FileInputStream fs = new FileInputStream("/home/ekasit/src/PluS-1.1.0/message.dat");
           StringBuffer sb = new StringBuffer();
           byte[] buf = new byte[40960];
           int len = 0;
           while ((len = fs.read(buf,0,buf.length)) > 0) {
              sb.append(new String(buf,0,len));
           }
           String data = sb.toString();
           DISDecoder decoder = new DISDecoder();
           BatchReply reply = decoder.getDecodedReply(data,request);

        }


}
